package com.yc.nfc;

import android.util.Log;

import com.yc.class_attendance.MyApp;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.InvalidParameterException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;

import static com.yc.nfc.Convert.ByteArrToHex;

/**
 * Created by Administrator on 2016-07-11.
 */
public class NfcClient {
    private static final String LOGTAG = "NfcClient";
    private SerialPort mSerialPort;
    private OutputStream mOutputStream;
    private InputStream mInputStream;
    private ReadThread mReadThread;
    private SendThread mSendThread;
    private String sPort = "/dev/s3c2410_serial0";
    private int iBaudRate = 9600;
    private boolean _isOpen = false;
    private byte[] _bLoopData = new byte[]{0x30};
    private int iDelay = 500;

    byte sector;
    byte[] mKeys;
    byte mSector;

    public NfcClient(byte sector, byte[] keys) {
        this("/dev/ttyS5", 115200);
        this.mSector = sector;
        this.mKeys = keys;
        sector = (byte) (mSector + 1);
    }

    public NfcClient(int com, byte sector, byte[] keys) {
        this("/dev/ttyS" + com, 115200);
        this.mSector = sector;
        this.mKeys = keys;
    }

    //----------------------------------------------------
    public NfcClient(String sPort, int iBaudRate) {
        this.sPort = sPort;
        this.iBaudRate = iBaudRate;
    }

    public NfcClient() {
        this("/dev/ttyS5", 115200);
    }

    public NfcClient(String sPort) {
        this(sPort, 9600);
    }

    public NfcClient(String sPort, String sBaudRate) {
        this(sPort, Integer.parseInt(sBaudRate));
    }

    //----------------------------------------------------
    public void open() throws SecurityException, IOException, InvalidParameterException {
        mSerialPort = new SerialPort(new File(sPort), iBaudRate, 0);
        mOutputStream = mSerialPort.getOutputStream();
        mInputStream = mSerialPort.getInputStream();
//        mReadThread = new ReadThread();
//        mReadThread.start();
//        mSendThread = new SendThread();
//        mSendThread.setSuspendFlag();
//        mSendThread.start();
        _isOpen = true;
    }

    //----------------------------------------------------
    public void close() {
//        if (mReadThread != null)
//            mReadThread.interrupt();
        if (mSerialPort != null) {
            mSerialPort.close();
            mSerialPort = null;
        }
        _isOpen = false;
    }

    //----------------------------------------------------
    public void send(byte[] bOutArray) {
        try {
            mOutputStream.write(bOutArray);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    //----------------------------------------------------
    public void sendHex(String sHex) {
        byte[] bOutArray = Convert.HexToByteArr(sHex);
        send(bOutArray);
    }

    //----------------------------------------------------
    public void sendTxt(String sTxt) {
        byte[] bOutArray = sTxt.getBytes();
        send(bOutArray);
    }

    //----------------------------------------------------
    private class ReadThread extends Thread {
        //private static final String STORAGE_SERVICE22 = "122133";

        @Override
        public void run() {
            super.run();
            while (!isInterrupted()) {
                //Log.i(STORAGE_SERVICE22, "44444444444444444444");
                try {
                    if (mInputStream == null) return;

                    byte[] buffer = new byte[512];
                    int size = mInputStream.read(buffer);
                    if (size > 6) {
                        ComBean ComRecData = new ComBean(sPort, buffer, size);

                    }
                    try {
                        Thread.sleep(50);//延时50ms
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                } catch (Throwable e) {
                    e.printStackTrace();
                    //Log.i(STORAGE_SERVICE22, "66666666666666666666666");
                    return;
                }
            }
        }
    }

    //----------------------------------------------------
    private class SendThread extends Thread {
        public boolean suspendFlag = true;// 控制线程的执行

        @Override
        public void run() {
            super.run();
            while (!isInterrupted()) {
                synchronized (this) {
                    while (suspendFlag) {
                        try {
                            wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
                send(getbLoopData());
                try {
                    Thread.sleep(iDelay);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        //线程暂停
        public void setSuspendFlag() {
            this.suspendFlag = true;
        }

        //唤醒线程
        public synchronized void setResume() {
            this.suspendFlag = false;
            notify();
        }
    }

    //----------------------------------------------------
    public int getBaudRate() {
        return iBaudRate;
    }

    public boolean setBaudRate(int iBaud) {
        if (_isOpen) {
            return false;
        } else {
            iBaudRate = iBaud;
            return true;
        }
    }

    public boolean setBaudRate(String sBaud) {
        int iBaud = Integer.parseInt(sBaud);
        return setBaudRate(iBaud);
    }

    //----------------------------------------------------
    public String getPort() {
        return sPort;
    }

    public boolean setPort(String sPort) {
        if (_isOpen) {
            return false;
        } else {
            this.sPort = sPort;
            return true;
        }
    }

    //----------------------------------------------------
    public boolean isOpen() {
        return _isOpen;
    }

    //----------------------------------------------------
    public byte[] getbLoopData() {
        return _bLoopData;
    }

    //----------------------------------------------------
    public void setbLoopData(byte[] bLoopData) {
        this._bLoopData = bLoopData;
    }

    //----------------------------------------------------
    public void setTxtLoopData(String sTxt) {
        this._bLoopData = sTxt.getBytes();
    }

    //----------------------------------------------------
    public void setHexLoopData(String sHex) {
        this._bLoopData = Convert.HexToByteArr(sHex);
    }

    //----------------------------------------------------
    public int getiDelay() {
        return iDelay;
    }

    //----------------------------------------------------
    public void setiDelay(int iDelay) {
        this.iDelay = iDelay;
    }

    //----------------------------------------------------
    public void startSend() {
        if (mSendThread != null) {
            mSendThread.setResume();
        }
    }

    //----------------------------------------------------
    public void stopSend() {
        if (mSendThread != null) {
            mSendThread.setSuspendFlag();
        }
    }
    //----------------------------------------------------

    public byte[] communication(String hex) {
        return communication(Convert.HexToByteArr(hex));
    }

    public byte[] communication(byte[] send) {
        send(send);
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        byte[] recv = null;
        int timeOut = 500;
        while (true) {
            if (timeOut <= 0)
                break;
            timeOut--;
            try {
                if (mInputStream.available() > 0) {
                    recv = receive();
                    break;
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return recv;
    }


    private byte[] receive() {
        byte[] buffer = new byte[512];
        byte[] bRec = null;
        try {
            int size = mInputStream.read(buffer);
            bRec = new byte[size];
            for (int i = 0; i < size; i++) {
                bRec[i] = buffer[i];
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return bRec;
    }

    byte[] name;
    private static final String ERROR_WAKEUP = "唤醒失败";
    private static final String ERROR_SELECT = "寻卡失败";
    private static final String ERROR_AUTH = "认证失败";
    private static final String ERROR_READ = "读卡失败";
    private static final String ERROR_WRITE = "写卡失败";
    boolean isWakeup = false;
    CardInfo info = new CardInfo();

    public CardInfo readMoney() {
        MyApp.getsInstance().setgiDestPort(0);
        if (!isWakeup) {
            isWakeup = nfcWakeup();
            if (!isWakeup) {
                Log.d(LOGTAG, ERROR_WAKEUP);
                return null;
            }
        }

        byte[] currentCardNum = nfcSelectCard();
        if (currentCardNum == null) {
            Log.d(LOGTAG, ERROR_SELECT);
            return null;
        }

        byte[] retPwd = new byte[6];

        CardUtils.getAnthCode(currentCardNum, mKeys, retPwd);
        byte[] cardId = new byte[4];
        cardId[0] = currentCardNum[3];
        cardId[1] = currentCardNum[2];
        cardId[2] = currentCardNum[1];
        cardId[3] = currentCardNum[0];
        String hexx = Convert.ByteArrToHex(cardId, null);
        BigInteger idd = new BigInteger(hexx, 16);
        if (String.valueOf(idd.longValue()).equals(MyApp.getsInstance().getCard())) {
            MyApp.getsInstance().setgiDestPort(1);
            return null;
        }
        if (info != null) {
            info.setCardID(cardId);
            MyApp.getsInstance().setCard(info.getCardID());
        }
        return info;
    }

    public CardInfo readPersonnel() {
        byte Sector = (byte) (mSector + 1);
        info.setFig("3");
        if (!isWakeup) {
            isWakeup = nfcWakeup();
            if (!isWakeup) {
                Log.d(LOGTAG, ERROR_WAKEUP);
                return null;
            }
        }

        byte[] currentCardNum = nfcSelectCard();
        if (currentCardNum == null) {
            Log.d(LOGTAG, ERROR_SELECT);
            return null;
        }

        byte[] retPwd = new byte[6];

        CardUtils.getAnthCode(currentCardNum, mKeys, retPwd);
        byte[] cardId = new byte[4];
        cardId[0] = currentCardNum[3];
        cardId[1] = currentCardNum[2];
        cardId[2] = currentCardNum[1];
        cardId[3] = currentCardNum[0];
        //认证卡片
        if (!nfcAnth(Sector, retPwd, currentCardNum, (byte) 0x60)) {
            Log.d(LOGTAG, ERROR_AUTH);
            info.setFig("1");
            return info;
        }


        byte[][] buffer = new byte[4][16];
        for (int i = 0; i < 4; i++) {
            if (!nfcReadBlock((byte) (Sector * 4 + i), buffer[i])) {
                Log.d(LOGTAG, ERROR_READ);
                return null;
            }
        }
        byte[] total = new byte[64];
        int c = 0;
        for (int j = 0; j < 4; j++) {
            for (int i = j * 16; i < (j + 1) * 16; i++) {
                if (c > 15) {
                    c = 0;
                }
                total[i] = buffer[j][c++];
            }
        }
        byte[] number = new byte[total[0]];
        name = new byte[total[total[0] + 1]];
        for (int i = 1; i < total[0] + 1; i++) {
            number[i - 1] = total[i];

        }
        for (int i = total[0] + 2; i < total[0] + 2 + total[total[0] + 1]; i++) {
            name[i - total[0] - 2] = total[i];
        }

        try {
            if (info != null) {
                info.setName(new String(name, "GB2312"));
                info.setCardID(cardId);
                info.setFig("0");
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();

        }
        return info;
    }


    private boolean nfcWakeup() {
        byte[] bRecvData = communication("55550000000000000000000000000000ff03fdD414011700".toString());
        if (bRecvData == null) {
            return false;
        }
        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);
        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[3] == (byte) 0x02) && (RecvData[4] == (byte) 0xfe) && (RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x15) && (RecvData[7] == (byte) 0x16)) {
                return true;
            }

        }
        return false;

    }

    /*
     * 寻找卡片
     * return :1-成功  其他-失败
     * */
    private byte[] nfcSelectCard() {

        byte[] bRecvData = communication("0000ff04fcd44a0100e100".toString());
        if (bRecvData == null) {
            return null;
        }
        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return null;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);
        if (RecvData == null) {
            Log.d(LOGTAG, "无效数据");
            return null;
        } else {
            int mLength = RecvData.length - 1;
            if (mLength > 2 && (RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
                if (mLength > 6 && (RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x4B)) {

                    if (mLength > 14 && (RecvData[9] == (byte) 0x00) && ((RecvData[10] == (byte) 0x02) || (RecvData[10] == (byte) 0x04))) {
                        byte[] UUID = new byte[4]; //物理卡号
                        for (int i = 0; i < 4; i++)
                            UUID[i] = RecvData[i + 13];
                        Log.d(LOGTAG, ByteArrToHex(UUID));

                        return UUID;
                    } else if (mLength > 14 && (RecvData[9] == (byte) 0x00) && (RecvData[10] == (byte) 0x44)) {
                        byte[] UUID = new byte[7];   //物理卡号
                        for (int i = 0; i < 7; i++)
                            UUID[i] = RecvData[i + 13];
                        Log.d(LOGTAG, ByteArrToHex(UUID));
                        return UUID;
                    } else {
                        Log.i(LOGTAG, "select failed");
                        return null;
                    }


                }

            }
            return null;
        }


    }

    /*
     * 卡片块写，M1卡规范，每一块只有16个字节
     * Addr-要写的卡片地址（4*扇区号，每个扇区有四个块，最大为15*4+3）,wBuffer-为一块的数据，写之前先拿到加密数据
     *
     * */
    private boolean nfcWriteBlock(byte Addr, byte[] wBuffer) {

        byte[] SendBuf = new byte[28];
        Arrays.fill(SendBuf, (byte) 0);
        if (Addr > 128) return false;

        SendBuf[0] = (byte) 0x00;
        SendBuf[1] = (byte) 0x00;
        SendBuf[2] = (byte) 0xff;
        SendBuf[3] = (byte) 0x15;
        SendBuf[4] = (byte) 0xEB;
        SendBuf[5] = (byte) 0xd4;
        SendBuf[6] = (byte) 0x40;
        SendBuf[7] = (byte) 0x01;
        SendBuf[8] = (byte) 0xa0;
        SendBuf[9] = Addr;
        for (int i = 0; i < 16; i++)
            SendBuf[10 + i] = wBuffer[i];
        byte checkSum = 0;
        for (int i = 0; i < 21; i++) {
            checkSum += SendBuf[i + 5];
        }
        SendBuf[26] = (byte) (~checkSum + 1);
        byte[] bRecvData = communication(SendBuf);

        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);

        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x41) && (RecvData[7] == (byte) 0x00)) {
                return true;
            }
        }
        return false;
    }

    /*
     * 卡片块读，M1卡规范，每一块只有16个字节
     * Addr-要写的卡片地址（4*扇区号，每个扇区有四个块，最大为15*4+3）,rBuffer-为一块的数据，写之前先拿到加密数据
     *
     * */
    private boolean nfcReadBlock(byte Addr, byte[] rBuffer) {

        byte[] SendBuf = new byte[12];
        Arrays.fill(SendBuf, (byte) 0);
        if (Addr > 128) return false;

        SendBuf[0] = (byte) 0x00;
        SendBuf[1] = (byte) 0x00;
        SendBuf[2] = (byte) 0xff;
        SendBuf[3] = (byte) 0x05;
        SendBuf[4] = (byte) 0xfB;
        SendBuf[5] = (byte) 0xd4;
        SendBuf[6] = (byte) 0x40;
        SendBuf[7] = (byte) 0x01;
        SendBuf[8] = (byte) 0x30;
        SendBuf[9] = Addr;


        int CheckSum;
        int i;
        for (i = 0, CheckSum = 0; i < 5; i++) {
            CheckSum += SendBuf[i + 5];
        }
        SendBuf[10] = (byte) (~CheckSum + 1);
        byte[] bRecvData = communication(SendBuf);

        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);

        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x41) && (RecvData[7] == (byte) 0x00)) {


                //取得得到块数据
                //byte[] CardBlock = new byte[16];
                for (i = 0; i < 16; i++)
                    rBuffer[i] = RecvData[i + 8];
                return true;
            }
        }
        return false;

    }

    /*
     * 认证卡片
     * Sector-扇区号（共16个扇区，0-15）;Keys-认证密钥;AnthType-认证类型（卡片有AB两个密码（60 A,61 B））
     * */
    private boolean nfcAnth(byte Sector, byte[] Keys, byte[] uuid, byte AnthType) {
        byte[] SendBuf = new byte[22];
        Arrays.fill(SendBuf, (byte) 0);

        if (Sector > 15) return false;

        SendBuf[0] = (byte) 0x00;
        SendBuf[1] = (byte) 0x00;
        SendBuf[2] = (byte) 0xff;
        SendBuf[3] = (byte) 0x0f;
        SendBuf[4] = (byte) 0xf1;
        SendBuf[5] = (byte) 0xd4;
        SendBuf[6] = (byte) 0x40;
        SendBuf[7] = (byte) 0x01;
        SendBuf[8] = (byte) AnthType;
        SendBuf[9] = (byte) (Sector * 4 + 3);

        for (int i = 0; i < 6; i++)
            SendBuf[10 + i] = Keys[i];
        for (int i = 0; i < 4; i++)
            SendBuf[16 + i] = uuid[i];


        int CheckSum;
        int i;
        for (i = 0, CheckSum = 0; i < 15; i++) {
            CheckSum += SendBuf[i + 5];
        }
        SendBuf[20] = (byte) (~CheckSum + 1);

        byte[] bRecvData = communication(SendBuf);

        if (bRecvData.length <= 6) {
            Log.d(LOGTAG, "无效数据");
            return false;
        }
        byte[] RecvData = Arrays.copyOfRange(bRecvData, 6, bRecvData.length);

        if ((RecvData[0] == (byte) 0x00) && (RecvData[1] == (byte) 0x00) && (RecvData[2] == (byte) 0xff)) {
            if ((RecvData[5] == (byte) 0xD5) && (RecvData[6] == (byte) 0x41) && (RecvData[7] == (byte) 0x00)) {
                return true;
            }
        }
        return false;

    }

    public void doReStart1() {
        //        communication("ff 14 11 06 15 0E 09 00 11 06 15 0E 00 00 11 06 15 0E 14 00 02 CD 00 E8".toString());


        int cyy = getYear("yyyy");
        int cMM = getYear("MM");
        int cdd = getYear("dd");

        int cHH = getYear("HH");
        int cmm = getYear("mm");
        int css = getYear("ss");

        //开机时间
        int oHH = 5;//getYear("HH");
        int omm = 0;
        int oss = 0;

        //关机时间
        int lHH = 23;//getYear("HH");
        int lmm = 0;//getYear("mm");
        int lss = 0;//getYear("ss");

        int oyStr1 = Integer.valueOf(String.valueOf(cyy).substring(2, 4));//注意动态
        Log.d("send", oyStr1 + "");
        //待长时间
        //关机时间
        int waitTime = 5;
        //1:组织指令数据
        byte[] btArray = new byte[24];

        btArray[0] = (byte) 170;//255  AA
        btArray[1] = (byte) 20;//20
        btArray[2] = (byte) 3;
        //系统当前时间yyyMMdd HH:mm:ss


        btArray[3] = (byte) oyStr1;
        btArray[4] = (byte) cMM;
        btArray[5] = (byte) cdd;
        btArray[6] = (byte) cHH;
        btArray[7] = (byte) cmm;
        btArray[8] = (byte) css;

        //开启时间yyyMMdd HH:mm:ss
        btArray[9] = (byte) oyStr1;
        btArray[10] = (byte) cMM;
        btArray[11] = (byte) cdd;
        btArray[12] = (byte) oHH;
        btArray[13] = (byte) omm;
        btArray[14] = (byte) oss;

        //关闭时间yyyMMdd HH:mm:ss
        btArray[15] = (byte) oyStr1;
        btArray[16] = (byte) cMM;
        btArray[17] = (byte) cdd;
        btArray[18] = (byte) lHH;
        btArray[19] = (byte) lmm;
        btArray[20] = (byte) lss;
        int total = 170 + 20 + 3 + oyStr1 + cMM + cdd + cHH + cmm + css + oyStr1 + cMM + cdd + oHH + omm + oss + oyStr1 + cMM + cdd + lHH + lmm + lss;
        if (total > 255) {
            Short sort = (short) total;
            byte[] sbts = shortToBytes(sort);
            btArray[21] = sbts[0];//低位参数 前21位参数之和
        } else {
            btArray[21] = (byte) total;
        }
        Log.d("send", total + " bootIime:" + oHH + "shutdown:" + lHH + "waitIimeTemp:" + waitTime);
        Log.d("send", btArray.toString());
        send(btArray);
    }

    public static byte[] shortToBytes(short n) {
        byte[] b = new byte[2];
        b[0] = (byte) (n & 0xff);
        b[1] = (byte) ((n >> 8) & 0xff);
        return b;
    }

    public int getYear(String tagete) {
        Calendar calendar = Calendar.getInstance();
        SimpleDateFormat yearSdf = new SimpleDateFormat(tagete);
        String yearString = yearSdf.format(calendar.getTime());
        return Integer.parseInt(yearString);
    }
}
